package edu.thu.thss.yxy.media;

import java.net.*;

import javax.media.*;
import javax.media.control.BufferControl;
import javax.media.protocol.DataSource;
import javax.media.rtp.*;
import javax.media.rtp.event.*;

import edu.thu.thss.yxy.util.Logger;

/**
 * AVReceiver to receive RTP transmission using the new RTP API.
 * 
 * @author Andy Gao
 * 
 */
public class RtpReceiver implements ReceiveStreamListener, SessionListener,
		ControllerListener {
	/**
	 * Our logger.
	 */
	private final Logger logger = Logger.getLogger(RtpReceiver.class);

	String sessions[] = null; // RTP会话字符串数组
	/**
	 * A list of currently active RTPManagers mapped against Local session
	 * addresses. The list is used by transmitters and receivers so that
	 * receiving and transmitting from the same port simultaneousl is possible
	 */
	private RTPManager rtpManagers[] = null;

	private MediaManager mediaManager;

//	private boolean dataReceived = false;

	private MediaSessionDescription mediaSessionDesc = null;

//	private Object dataSync = new Object(); // 同步对象

	public RtpReceiver(String sessionDescription) {
		mediaSessionDesc = new MediaSessionDescription(sessionDescription);

		mediaSessionDesc.setAudioFormat("mpegaudio/rtp, 48000.0 hz, 16-bit, mono");
		mediaSessionDesc.setVideoFormat("h263/rtp");
		mediaSessionDesc.setTransportProtocol("udp");
		if (mediaSessionDesc.getVideoFormat() != null
				&& mediaSessionDesc.getAudioFormat() != null)
			rtpManagers = new RTPManager[2];
		else if (mediaSessionDesc.getVideoFormat() != null
				|| mediaSessionDesc.getAudioFormat() != null)
			rtpManagers = new RTPManager[1];
	}
	
	 /**
     * Constructor for Receiver
     * @param session - the session Description containing the address, the port, the Time To Live
     * the video format, the audio format and the transport protocol
     */
	public RtpReceiver(MediaSessionDescription sessionDesc){
		mediaSessionDesc = sessionDesc;
		if (mediaSessionDesc.getVideoFormat() != null
				&& mediaSessionDesc.getAudioFormat() != null)
			rtpManagers = new RTPManager[2];
		else if (mediaSessionDesc.getVideoFormat() != null
				|| mediaSessionDesc.getAudioFormat() != null)
			rtpManagers = new RTPManager[1];
	}
	
	public void setMediaManager(MediaManager mediaManager) {
		this.mediaManager = mediaManager;
	}

	/**
	 * Initialize the RTP Mamagers an wait for the data There is one by stream
	 * 
	 * @return false if the rtpmanagers can't be initialized or if no data was
	 *         received
	 */
	protected boolean initialize() {
		if (rtpManagers == null)
			return false;
		try {
			for (int i = 0; i < rtpManagers.length; i++) {
				if (i == 0) {
					// Creates a new instance of RTPManager
					rtpManagers[i] = (RTPManager) RTPManager.newInstance();
					// create the local endpoint for the local interface on the
					// port given in parameter
					int localPort = mediaSessionDesc.getLocalPort() + 2 * i;
					// specify the remote endpoint of this unicast session on
					// the port given in parameter
					int destPort = mediaSessionDesc.getDestinationPort() + 2 * i;
					
					SessionAddress localAddr = new SessionAddress(InetAddress.getLocalHost(),
							localPort);
					rtpManagers[i].addSessionListener(this);
					// add the ReceiveStreamListener to receive data
					rtpManagers[i].addReceiveStreamListener(this);
					// initialize the RTPManager, so the session

					logger.info("[RtpReceiver]- Init UDP Transmitter");
					rtpManagers[i].initialize(localAddr);

					// You can try out some other buffer size to see
					// if you can get better smoothness.
					BufferControl bc = (BufferControl) rtpManagers[i]
							.getControl("javax.media.control.BufferControl");
					if (bc != null) {
						if (i == 0) {
							bc.setBufferLength(0);
							// bc.setMinimumThreshold(0);
							logger.info("[RtpReceiver]- Threshold enabled : "
									+ bc.getEnabledThreshold());
							logger.info("[RtpReceiver]- buf length : "
									+ bc.getBufferLength());
							logger.info("[RtpReceiver]- minimum Threshold : "
									+ bc.getMinimumThreshold());
						} else {
							bc.setBufferLength(BufferControl.MAX_VALUE);
							// bc.setMinimumThreshold(BufferControl.MAX_VALUE);
							logger.info("[RtpReceiver]- buf length : "
									+ bc.getBufferLength());
							logger.info("[RtpReceiver]- minimum Threshold : "
									+ bc.getMinimumThreshold());
						}
					}
					if (mediaSessionDesc.getTransportProtocol().toLowerCase()
							.equals("udp")) {
						SessionAddress destAddr = new SessionAddress(
								InetAddress.getByName(mediaSessionDesc
										.getAddress()), destPort);
						rtpManagers[i].addTarget(destAddr);
					}
					logger.info("[RtpReceiver]- Open RTP session for: Address: "
							+ mediaSessionDesc.getAddress() + " localPort: "
							+ localPort + " destPort : " + destPort
							+ " Time To Live: "
							+ mediaSessionDesc.getTimeToLive());
				}
			}
		} catch (Exception e) {
			logger.info("[RtpReceiver]- Cannot create the RTP Session: ");
			e.printStackTrace();
			return false;
		}
		 
		logger.info(" [RtpReceiver]- - Waiting for RTP data to arrive...");
		return true;

	}

	public synchronized void update(ReceiveStreamEvent evt) {
		@SuppressWarnings("unused")
		RTPManager mgr = (RTPManager) evt.getSource();
		Participant participant = evt.getParticipant(); // could be null.
		ReceiveStream stream = evt.getReceiveStream(); // could be null.
		if (evt instanceof NewReceiveStreamEvent) {
			try {
				// Pull out the stream
				stream = ((NewReceiveStreamEvent) evt).getReceiveStream();
				DataSource ds = stream.getDataSource();

				// Find out the formats.
				RTPControl ctl = (RTPControl) ds
						.getControl("javax.media.rtp.RTPControl");
				if (logger.isDebugEnabled()) {
					if (ctl != null) {
						logger.debug("[RtpReceiver]- Recevied new RTP stream: "
								+ ctl.getFormat());
					} else {
						logger.debug("[RtpReceiver]- Recevied new RTP stream");
					}
				}
				Player p = Manager.createPlayer(ds);
				if (p == null)
					return;
				p.addControllerListener(this);
				p.realize();
			} catch (Exception e) {
				logger.error("NewReceiveStreamEvent exception ", e);
				return;
			}
		} else if (evt instanceof StreamMappedEvent) {
			if (stream != null && stream.getDataSource() != null) {
				DataSource ds = stream.getDataSource();
				// Find out the formats.
				RTPControl ctl = (RTPControl) ds
						.getControl("javax.media.rtp.RTPControl");
				if (logger.isDebugEnabled()) {
					String msg = "[RtpReceiver]- The previously unidentified stream ";
					if (ctl != null) {
						msg += ctl.getFormat();
					}
					msg += " had now been identified as sent by: "
							+ participant.getCNAME();
					logger.debug(msg);
				}
			}
		} else if (evt instanceof ByeEvent) {
			logger.debug("Got \"bye\" from: " + participant.getCNAME());
		}

	}

	public synchronized void update(SessionEvent evt) {
		if (evt instanceof NewParticipantEvent) {
			Participant p = ((NewParticipantEvent) evt).getParticipant();
			logger.info("[RtpReceiver]- A new participant had just joined: " + p.getCNAME());
		}else{
			logger.info("[RtpReceiver] received a the following JMF Session event - ");
			logger.info(evt.getClass().getName());
		}
	}
	
	public void stop() {
		if (rtpManagers == null)
			return;
		// close the RTP session.
		for (int i = 0; i < rtpManagers.length; i++) {
			if (rtpManagers[i] != null) {
				rtpManagers[i].removeTargets("Closing session");
				rtpManagers[i].dispose();
				rtpManagers[i] = null;
			}
		}
	}

	/**
	 * ControllerListener for the Players.
	 */
	public synchronized void controllerUpdate(ControllerEvent ce) {
		Player p = (Player) ce.getSourceController();
		if (p == null) {
			return;
		}

		// Get this when the internal players are realized.
		if (ce instanceof RealizeCompleteEvent) {
			logger.debug("[RtpReceiver]- A player was realized and will be started.");
			p.start();
		}
		if (ce instanceof StartEvent) {
			logger.debug("[RtpReceiver]- Received a StartEvent");
			mediaManager.fireMediaStreamReceived(p);
		}
		if (ce instanceof ControllerErrorEvent) {
			logger.error("[RtpReceiver]- The following error was reported while starting a player"
							+ ce);
		}
		if (ce instanceof ControllerClosedEvent) {
			logger.debug("[RtpReceiver]- Received a ControllerClosedEvent");
			mediaManager.fireMediaStatusChanged();
		}
	}
}
